package com.project.pdf;

import static java.lang.System.out;

import java.awt.Color;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.ResourceBundle;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

import net.miginfocom.swing.MigLayout;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.h819.core.file.MyFileNameExtensionFilter;
import com.h819.core.file.MyFileUtils;
import com.itextpdf.text.exceptions.BadPasswordException;
import com.itextpdf.text.pdf.PdfReader;

/**
 * @Title: SwingWorkerExampleJPanel.java
 * @Description: TODO(演示 SwingWorker 的用法。)
 * @author H819
 * @date 2011-2-24
 * @version V1.0
 */

public class SwingWorkerJPanel extends JPanel implements PropertyChangeListener {
	private static final ResourceBundle BUNDLE = ResourceBundle
			.getBundle("com.project.pdf.messages"); //$NON-NLS-1$
	private JLabel lblLabel;
	private JProgressBar progressBar;
	// ProgressMonitor 不好操控，不用这个组件
	// private ProgressMonitor progressMonitor;
	private JButton btnStart;
	private JButton btnCancel;
	/**
	 * @wbp.nonvisual location=42,362
	 */
	private final JFileChooser fileChooser = new JFileChooser("D:\\");

	// 提示信息
	private String message = "";
	private Task task;

	private static Logger log = Logger.getLogger(SwingWorkerJPanel.class);

	// 待处理的任务
	File selectedFile;
	ArrayList<String> files = null;
	String descPdfFileDirOpen = "";
	private JLabel label;

	Runtime rt = Runtime.getRuntime();

	// 记录最终处理完成的文件总数
	int decrpiedFileNumber = 0;
	
	

	// 拷贝资源文件
	String[] resources = new String[] { "/license.dat",
			"/pdfdecrypt.exe", "/SkinMagic.dll" };
	String[] resourcesNames = MyFileUtils
			.copyResourceFileFromJarLibToTmpDir(resources,
					"pdftempdir", SwingWorkerJPanel.class);

	/**
	 * Create the panel.
	 */
	public SwingWorkerJPanel() {

		setLayout(new MigLayout("", "[grow]", "[][][][][][][][]"));

		lblLabel = new JLabel(" ");
		add(lblLabel, "cell 0 1");

		fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

		btnStart = new JButton("选择文件夹");
		btnStart.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {

				int returnValue = fileChooser.showOpenDialog(null);

				if (returnValue == JFileChooser.APPROVE_OPTION) {

					// 源文件，赋值给全局变量，便于 task 处理
					selectedFile = fileChooser.getSelectedFile();

					out.println(selectedFile);

					if (StringUtils.contains(selectedFile.getAbsolutePath(),
							" ")) {
						// 待破解的文件夹不能包含空格，有空格时，影响命令行执行
						JOptionPane.showConfirmDialog(SwingWorkerJPanel.this,
								"文件夹路径不能包含空格，请把要破解的文件夹转移到磁盘根目录。", "文件夹路径错误",
								JOptionPane.CLOSED_OPTION);

						return;
					}

					// 此处弹出对话框
					int n = JOptionPane.showConfirmDialog(
							SwingWorkerJPanel.this,
							"确认破解 " + selectedFile.getPath() + " 中的 PDF 文件么？",
							"确认所选文件夹", JOptionPane.YES_NO_OPTION);

					if (n == JOptionPane.CANCEL_OPTION
							|| n == JOptionPane.CLOSED_OPTION
							|| n == JOptionPane.NO_OPTION)
						return;

					// if (response == JOptionPane.NO_OPTION) {
					// System.out.println("No button clicked");
					// } else if (response == JOptionPane.YES_OPTION) {
					// System.out.println("Yes button clicked");
					// } else if (response == JOptionPane.CLOSED_OPTION) {
					// System.out.println("JOptionPane closed");
					// }

					
					try {
						files = MyFileUtils.getFileNames(selectedFile,
								new MyFileNameExtensionFilter(
										new String[] { "pdf" }));
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}

					// for (String s : files)
					// out.println(s);

					if (files == null || files.size() == 0) {

						// 此处弹出对话框
						// 此处弹出对话框
						JOptionPane.showMessageDialog(SwingWorkerJPanel.this,
								"文件夹中没有 pdf 文件");

						return;
					}

				}

				progressBar.setValue(0);

				// start
				task = new Task();
				// 添加监听器，实现方法是下文的 propertyChange 方法。
				task.addPropertyChangeListener(SwingWorkerJPanel.this);

				btnStart.setEnabled(false);

				task.execute();

				log.info("start");

			}
		});

		progressBar = new JProgressBar(0, 100);
		progressBar.setStringPainted(true);
		progressBar.setBackground(Color.orange);
		progressBar.setForeground(Color.red);
		add(progressBar, "cell 0 2");

		label = new JLabel(BUNDLE.getString("Pdfdecrypt.message")); //$NON-NLS-1$
		add(label, "cell 0 3");
		add(btnStart, "flowx,cell 0 7");

		btnCancel = new JButton("取消选择");
		btnCancel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {

				// cancel
				task.cancel(true);

			}
		});

		add(btnCancel, "cell 0 7,aligny bottom");
	}

	/**
	 * 执行任务的内部类，想要执行的操作，在这里执行。
	 * 
	 * @ClassName: Task
	 * @Description: TODO(执行任务的内部类，想要执行的操作，在这里执行。)
	 * @author h819
	 * @date 2010-10-6 下午10:31:31
	 * @version V1.0
	 */

	class Task extends SwingWorker<Void, Void> {

		@Override
		// 执行任务的方法 ，当 progress =100 时，自动停止
		public Void doInBackground() {
			Random random = new Random();

			// progress 是 SwingWorker 默认的约束属性，下文的
			// propertyChange(PropertyChangeEvent evt)
			// 也是 SwingWorker 中的默认方法，该方法跟踪 progress 变化。
			// progress 变量是一个从0到100的整型值。
			// progress 的变化通过 setProgress(0)体现。

			// 创建一个临时变量，模拟 SwingWorker 默认的约束属性 progress，为 int 类型
			// 使其变化范围为 [0,100]，每次增长步长为 1
			// 即在 100 次的变化范围内，完成任务

			// 所以，主要是寻找 progress 的增长时机
			int progress = 0;
			setProgress(0);

			String descPdfFileDir = FilenameUtils
					.separatorsToSystem(selectedFile.getParent()
							+ File.separator + "decryptPdfDir");
			String badFileDir = FilenameUtils.separatorsToSystem(selectedFile
					.getParent() + File.separator + "badPdfDir");

			descPdfFileDirOpen = descPdfFileDir;

			log.info("srcPdfFileDir :" + selectedFile.getAbsolutePath());
			log.info("descPdfFileDir :" + descPdfFileDir);
			log.info("badFileDir :" + badFileDir);

			// 目标盘剩余空间
			long prefixDiskFreeSize = selectedFile.getFreeSpace();
			// 源文件夹大小
			long srcSize = FileUtils.sizeOf(selectedFile);

			log.info("diskFreeSize :" + prefixDiskFreeSize / 1000000.00 + " M");
			log.info("task will use :" + srcSize / 1000000.00 + " M");

			if (prefixDiskFreeSize < srcSize) {
				log.info(descPdfFileDir + " has not enoght disk size .");

				return null;
			}


			// 得到可执行文件路径
			String execommand = resourcesNames[1];

			// String execommand = SystemUtils.getJavaIoTmpDir()
			// + "/pdfdecrypt/pdfdecrypt.exe";

			// 拷贝资源文件到临时目录 完成

			// 创建目录
			try {
				MyFileUtils.forceMkdir(new File(descPdfFileDir));
				MyFileUtils.forceMkdir(new File(badFileDir));
			} catch (IOException e3) {
				// TODO Auto-generated catch block
				e3.printStackTrace();
			}

			// 任务总量

			int count = 0;

			if (files != null)
				count = files.size();

			// 添加破解数量限制
			// if (count > 30)
			// count = 30;

			// progressBar 和 progressMonitor 增长步长.
			// 因为总量为 100%,增长 100 次，达到总数。
			float countTofloat = count * 1.0f;
			int step = Math.round(countTofloat / 100f);
			// 开始时机
			int i = 1;

			// 怎么设计一个过程，是 progress 在 100 次的增长过程中，完成
			// 所有的任务。

			// 只有 i < count 条件和任务相关。不同的任务，设置不同的停止条件即可
			while (progress < 100 && !isCancelled() && i <= count) {

				// 如果处理的任务耗时很短，就加上一段延迟的代码，便于看清楚执行过程。
				try {
					Thread.sleep(random.nextInt(50));

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				// 已经到达任务总数，而 progress 还没到 100，直接赋值 progress 为 100
				if (i == count) {
					progress = 100;
					out.println(" **************** update progress = "
							+ progress);

					// 更新参数
					setProgress(progress);
				}

				/**
				 * 仅在下面处理任务即可
				 */

				log.info("doSomething(): i = " + i);

				String absoluteFile = files.get(i - 1);
				log.info("fileName :" + absoluteFile);

				File f = new File(absoluteFile);

				// 去掉文件名中的空格，否则命令行会发生错误
				log.info("contains blank ?"
						+ StringUtils.contains(absoluteFile, " "));
				if (StringUtils.contains(absoluteFile, " ")) {
					String fname = StringUtils.replace(absoluteFile, " ", "~");
					f.renameTo(new File(fname));
					f = new File(fname);
					absoluteFile = f.getAbsolutePath();
				}

				String baseFileName = f.getName();

				log.info("baseFileName: " + baseFileName);

				File badFile = new File(badFileDir + File.separator
						+ baseFileName);

				// 损坏的 0 字节文件，文件体积大于 40 M， 直接拷贝到统一的文件夹
				if (FileUtils.sizeOf(f) == 0
						|| FileUtils.sizeOf(f) > 40 * 1000000) {
					// 拷贝
					log.info("file size =0 or size >40M ,copy \""
							+ f.getAbsolutePath() + "\" to \""
							+ badFile.getAbsolutePath() + "\"");

					try {
						FileUtils.copyFile(f, badFile);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					i++;
					continue;
				}

				PdfReader reader = null;

				try {

					reader = new PdfReader(f.getAbsolutePath());

				} catch (BadPasswordException e) {
					// 有打开密码的文件，不能破解，统一拷贝至一个文件夹。
					e.printStackTrace();
					log.info("file has user password,can'nt decrypt.copy \""
							+ f.getAbsolutePath() + "\" to \"" + badFile + "\"");

					// 拷贝
					try {
						FileUtils.copyFile(f, badFile);
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					i++;
					continue;
				} catch (Exception e2) {

					// 其他的任何处理不了的异常，均拷贝

					log.info("file decrack exception! copy \""
							+ f.getAbsolutePath() + "\" to \""
							+ badFile.getAbsolutePath() + "\"");

					// 拷贝
					try {
						FileUtils.copyFile(f, badFile);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					e2.printStackTrace();
					i++;
					continue;

				}

				// 目标文件
				String descFile = descPdfFileDir
						+ File.separator
						+ StringUtils.remove(absoluteFile,
								selectedFile.getAbsolutePath());

				// 规范一下路径
				descFile = FilenameUtils.separatorsToSystem(descFile);
				String descDir = new File(descFile).getParent();

				try {
					MyFileUtils.forceMkdir(new File(descDir));
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				// log.info("fileDesc name :" + fileDesc.getAbsolutePath());

				if (!reader.isEncrypted()) {// 未加密的文件，直接拷贝到目标文件夹
					try {
						FileUtils.copyFile(f, new File(descFile));
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					log.info("not encrypted,copy \"" + f.getAbsolutePath()
							+ "\" to \"" + descFile + "\"");
					i++;
					continue;
				}

				String cmd = execommand + " -i " + f.getAbsolutePath() + " -o "
						+ descFile;
				cmd = FilenameUtils.separatorsToSystem(cmd);

				log.info("encrypted " + f.getAbsolutePath() + " to " + descFile);

				log.info("cmd is :" + cmd);
				try {

					rt.exec(cmd);
					// Runtime.getRuntime().exec(cmd);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				// 关闭处理完成的文件
				reader.close();

				// 返回提示信息
				message = "正在解密: " + absoluteFile;

				/**
				 * 仅在这上面处理任务即可
				 */

				// ================ 下面这段代码不用管 ===================

				// 根据 i 的变化，分不同的情况，更新 progress ，和具体的业务没有关系
				// 为了进度条显示的更加合理，把任务数量分成了几个区间。
				// 否则:
				// 当任务很少的时候，进度条没有到 100%，任务已经完成了。
				// 当任务很多的时候，进度条已经进行到 到 100%，但任务还没有完成。

				if (count <= 100) {
					int divisor = 100 / count;

					if (i * divisor <= 100 && i <= count) {
						progress = i * divisor;
						if (i == count) {
							progress = 100;
							out.println(" **************** update progress = "
									+ progress);
						}

						// 更新参数
						setProgress(progress);
					}

				} else if (100 < count) {

					// 如果 progress 已经接近 100 ，但任务总数还没完成，则不更新 progress，继续处理任务。
					if (progress == 98 && i < count) {
						i++;
						continue;

					}

					if (i % step == 0) {// progress 增长 1
										// 的时机，经过四舍五入 Math.round
						progress++;
						out.println(" **************** update progress = "
								+ progress);
					}

					// 更新参数
					setProgress(progress);

				}

				i++;
				// ================ 上面这段代码不用管 ===================
			}

			decrpiedFileNumber = i - 1;

			return null;
		}

		/**
		 * 任务完成之后的动作。
		 */
		@Override
		public void done() {
			Toolkit.getDefaultToolkit().beep();
			log.info("task done.");
			btnStart.setEnabled(true);

			// lable 信息更新
			lblLabel.setText("完成，共解密文件 " + decrpiedFileNumber + " 个");

			// 打开目标文件夹
			try {
				java.awt.Desktop.getDesktop()
						.open(new File(descPdfFileDirOpen));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * 跟踪 Task 类中 progress 变量变化，当 progress 变量 变化时，直接触发这个方法，更新组件信息。
	 * 
	 * progress 变化范围为[0,100]
	 * 
	 */
	public void propertyChange(PropertyChangeEvent evt) {
		if ("progress" == evt.getPropertyName()) {

			// 下面这个 progress ，数值取自 Task 类中的默认变量 progress
			int progress = (Integer) evt.getNewValue();

			// taskOutput.append(message);
			if (task.isCancelled()) {
				log.info("task isCancelled()");
				btnStart.setEnabled(true);
			}

			// lable 信息更新
			lblLabel.setText(this.message);

			// progressBar 信息更新
			progressBar.setValue(progress);

		}

	}

}
